package database

import (
	"JoRedis/lib/logger"
	"JoRedis/util"
	//"JoRedis/util"
	"fmt"
	"sync"
	"time"
)

type TTL struct {
	ttlDictPtr *TTLDict
	heapPtr    *Heap
	stopChn    chan struct{}
	// Key对应的时间差 key:Key, value:槽-时间
	slotTimeMap sync.Map
	syncOnce    sync.Once
	// 下一次要触发的槽-时间
	nextSlotTime int
	// 改变定时器时间的通知
	changeTimerChn chan struct{}
	// 上一次触发的槽-时间
	lastSlotTime int
}

func MakeTTL() *TTL {
	ttlDictPtr := MakeTTLDict()
	heapPtr := MakeMinHeap()
	ttlPtr := &TTL{
		ttlDictPtr:     ttlDictPtr,
		heapPtr:        heapPtr,
		syncOnce:       sync.Once{},
		nextSlotTime:   -1,
		changeTimerChn: make(chan struct{}),
		lastSlotTime:   0,
	}

	ttlPtr.syncOnce.Do(ttlPtr.GoStart)

	return ttlPtr
}

// Add 添加超时
func (this *TTL) Add(key string, expire int) {
	slotTime := this.ttlDictPtr.Add(key, expire, this.lastSlotTime)
	this.heapPtr.Put(slotTime)
	this.slotTimeMap.Store(key, slotTime)
	// 判断刚添加的expire是否比下一次要执行的超时还要短, 优先执行短的expire
	if this.nextSlotTime != -1 && slotTime < this.nextSlotTime-this.lastSlotTime {
		// util.PrintTime(fmt.Sprintf("定时器重置: expire:%d, slotTime:%d, nextSlotTime:%d, lastSlotTime:%d,dif:%d\n", expire, slotTime, this.nextSlotTime, this.lastSlotTime, this.nextSlotTime-this.lastSlotTime))
		this.calResetTime()
		this.changeTimerChn <- struct{}{}
	}
}

// Update 更新超时
func (this *TTL) Update(key string, expire int) {
	slotTimeItf, ok := this.slotTimeMap.Load(key)
	if ok {
		oldSlotTime := slotTimeItf.(int)
		// 更新槽
		slotTime := this.ttlDictPtr.Update(key, expire, oldSlotTime, this.lastSlotTime)
		this.heapPtr.Put(slotTime)
		this.slotTimeMap.Store(key, slotTime)
	} else {
		this.Add(key, expire)
	}
}

// GoStart 开始超时定时器
func (this *TTL) GoStart() {
	slotTime, timeReset := this.calResetTime()
	timePtr := time.NewTimer(time.Second * time.Duration(timeReset))
	go func() {
		for {
			select {
			case <-timePtr.C:
				// 取出超时时间槽中的key
				this.heapPtr.Pop()
				this.lastSlotTime = slotTime
				keySlc := this.ttlDictPtr.Get(slotTime)
				this.ttlDictPtr.Delete(slotTime)
				util.PrintTime("删除槽: ")
				for _, key := range keySlc {
					this.slotTimeMap.Delete(key)
					fmt.Printf(" %s, ", key)
				}
				fmt.Println()
				slotTime, timeReset = this.calResetTime()
				timePtr.Reset(time.Second * time.Duration(timeReset))
			case <-this.stopChn:
				// 退出
				break
			case <-this.changeTimerChn:
				// util.PrintTime("接收到定时器重置的通知")
				slotTime, timeReset = this.calResetTime()
				timePtr.Reset(time.Second * time.Duration(timeReset))
			}
		}
	}()
}

// Stop 停止超时定时器
func (this *TTL) Stop() {
	this.stopChn <- struct{}{}
}

// calSubTime 计算定时器的重置时间
func (this *TTL) calResetTime() (int, int) {
	slotTimeItf := this.heapPtr.Top()
	slotTime, ok := slotTimeItf.(int)
	var timeReset int
	if !ok {
		logger.Warn("TTL 时间格式异常", slotTimeItf)
		// 进入24小时等待状态
		slotTime = 24 * 60 * 60
		timeReset = 24 * 60 * 60
	} else {
		// slotSubTime := util.TimeSince(this.ttlDictPtr.baseTime,time.Now())
		// 定时器要重置的时间(秒)
		timeReset = slotTime - this.lastSlotTime
	}
	// util.PrintTime(fmt.Sprintf("定时器计算: nextSlotTime:%d, timeReset:%d, lastSlotTime:%d", slotTime, timeReset, this.lastSlotTime))
	this.nextSlotTime = slotTime
	return slotTime, timeReset
}
